from celery import shared_task
from django.utils import timezone
from .models import Host, HostMonitorData
import paramiko
import psutil
import time
import socket
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync


@shared_task
def collect_host_info(host_id):
    """收集主机信息的异步任务"""
    try:
        host = Host.objects.get(id=host_id)
        
        # 建立SSH连接
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        
        if host.ssh_private_key:
            key = paramiko.RSAKey.from_private_key_string(host.ssh_private_key)
            ssh.connect(
                hostname=host.ip_address,
                port=host.ssh_port,
                username=host.ssh_username,
                pkey=key,
                timeout=10
            )
        else:
            ssh.connect(
                hostname=host.ip_address,
                port=host.ssh_port,
                username=host.ssh_username,
                password=host.ssh_password,
                timeout=10
            )
        
        # 收集系统信息
        commands = {
            'os_info': 'cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d \'"\'',
            'kernel': 'uname -r',
            'cpu_cores': 'nproc',
            'memory_total': 'free -m | grep Mem | awk \'{print $2}\'',
            'disk_total': 'df -h / | tail -1 | awk \'{print $2}\' | sed \'s/G//\'',
            'cpu_usage': 'top -bn1 | grep "Cpu(s)" | awk \'{print $2}\' | cut -d% -f1',
            'memory_usage': 'free | grep Mem | awk \'{printf("%.1f", $3/$2 * 100.0)}\'',
            'disk_usage': 'df -h / | tail -1 | awk \'{print $5}\' | sed \'s/%//\'',
            'load_average': 'uptime | awk -F\'load average:\' \'{print $2}\' | awk \'{print $1}\' | sed \'s/,//\'',
        }
        
        results = {}
        for key, command in commands.items():
            stdin, stdout, stderr = ssh.exec_command(command)
            output = stdout.read().decode().strip()
            results[key] = output
        
        ssh.close()
        
        # 更新主机基本信息
        if results.get('os_info'):
            host.os_version = results['os_info']
        if results.get('kernel'):
            host.kernel_version = results['kernel']
        if results.get('cpu_cores'):
            try:
                host.cpu_cores = int(results['cpu_cores'])
            except ValueError:
                pass
        if results.get('memory_total'):
            try:
                host.memory_total = int(results['memory_total'])
            except ValueError:
                pass
        if results.get('disk_total'):
            try:
                host.disk_total = int(float(results['disk_total']))
            except ValueError:
                pass
        
        host.status = 'online'
        host.last_check_time = timezone.now()
        host.save()
        
        # 保存监控数据
        try:
            monitor_data = HostMonitorData.objects.create(
                host=host,
                cpu_usage=float(results.get('cpu_usage', 0)),
                memory_usage=float(results.get('memory_usage', 0)),
                disk_usage=float(results.get('disk_usage', 0)),
                network_in=0,  # 简化，实际需要计算网络流量
                network_out=0,
                load_average=float(results.get('load_average', 0))
            )
        except (ValueError, TypeError):
            # 如果数据转换失败，创建默认监控数据
            monitor_data = HostMonitorData.objects.create(
                host=host,
                cpu_usage=0,
                memory_usage=0,
                disk_usage=0,
                network_in=0,
                network_out=0,
                load_average=0
            )
        
        return {
            'success': True,
            'host_id': host_id,
            'message': '主机信息收集成功'
        }
        
    except Host.DoesNotExist:
        return {
            'success': False,
            'host_id': host_id,
            'message': '主机不存在'
        }
    except Exception as e:
        # 更新主机状态为离线
        try:
            host = Host.objects.get(id=host_id)
            host.status = 'offline'
            host.last_check_time = timezone.now()
            host.save()
        except Host.DoesNotExist:
            pass
        
        return {
            'success': False,
            'host_id': host_id,
            'message': f'收集失败: {str(e)}'
        }


@shared_task
def batch_collect_host_info():
    """批量收集所有主机信息"""
    hosts = Host.objects.filter(status__in=['online', 'offline'])
    results = []
    
    for host in hosts:
        result = collect_host_info.delay(host.id)
        results.append({
            'host_id': host.id,
            'task_id': result.id
        })
    
    return {
        'message': f'已启动 {len(results)} 个主机信息收集任务',
        'tasks': results
    }


@shared_task
def cleanup_old_monitor_data():
    """清理旧的监控数据（保留最近30天）"""
    from datetime import timedelta
    
    cutoff_date = timezone.now() - timedelta(days=30)
    deleted_count = HostMonitorData.objects.filter(
        collected_at__lt=cutoff_date
    ).delete()[0]
    
    return {
        'message': f'已清理 {deleted_count} 条旧监控数据'
    }


@shared_task
def check_single_host_status(host_id):
    """检测单个主机状态"""
    try:
        host = Host.objects.get(id=host_id)
        old_status = host.status
        new_status = _check_host_status(host)

        if old_status != new_status:
            # 状态发生变化，更新数据库
            host.status = new_status
            host.last_check_time = timezone.now()
            host.save(update_fields=['status', 'last_check_time'])

            # 广播状态变化
            _broadcast_status_change(host)

            return {
                'host_id': host_id,
                'hostname': host.hostname,
                'old_status': old_status,
                'new_status': new_status,
                'changed': True
            }

        return {
            'host_id': host_id,
            'hostname': host.hostname,
            'status': new_status,
            'changed': False
        }

    except Host.DoesNotExist:
        return {'error': f'主机 {host_id} 不存在'}
    except Exception as e:
        return {'error': f'检测主机 {host_id} 失败: {str(e)}'}


@shared_task
def check_all_hosts_status():
    """检测所有主机状态"""
    hosts = Host.objects.all()
    results = []

    for host in hosts:
        # 直接在当前任务中检测，避免创建过多子任务
        try:
            old_status = host.status
            new_status = _check_host_status(host)

            if old_status != new_status:
                # 状态发生变化，更新数据库
                host.status = new_status
                host.last_check_time = timezone.now()
                host.save(update_fields=['status', 'last_check_time'])

                # 广播状态变化
                _broadcast_status_change(host)

                results.append({
                    'host_id': host.id,
                    'hostname': host.hostname,
                    'old_status': old_status,
                    'new_status': new_status,
                    'changed': True
                })
            else:
                results.append({
                    'host_id': host.id,
                    'hostname': host.hostname,
                    'status': new_status,
                    'changed': False
                })

        except Exception as e:
            results.append({
                'host_id': host.id,
                'hostname': host.hostname,
                'error': str(e)
            })

    changed_count = len([r for r in results if r.get('changed')])
    return {
        'message': f'检测完成，共 {len(hosts)} 台主机，{changed_count} 台状态发生变化',
        'results': results
    }


def _check_host_status(host):
    """检测单个主机状态的内部函数"""
    try:
        # 端口检测
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        result = sock.connect_ex((host.ip_address, host.ssh_port or 22))
        sock.close()

        if result != 0:
            return 'offline'

        # SSH连接测试
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        if host.ssh_private_key:
            key = paramiko.RSAKey.from_private_key_string(host.ssh_private_key)
            ssh.connect(
                hostname=host.ip_address,
                port=host.ssh_port,
                username=host.ssh_username,
                pkey=key,
                timeout=5
            )
        else:
            ssh.connect(
                hostname=host.ip_address,
                port=host.ssh_port,
                username=host.ssh_username,
                password=host.ssh_password,
                timeout=5
            )

        # 执行简单命令测试
        stdin, stdout, stderr = ssh.exec_command('echo "status_check"')
        output = stdout.read().decode().strip()
        ssh.close()

        return 'online' if output == "status_check" else 'error'

    except Exception as e:
        return 'offline'


def _broadcast_status_change(host):
    """广播主机状态变化"""
    channel_layer = get_channel_layer()
    if channel_layer:
        try:
            async_to_sync(channel_layer.group_send)(
                "host_status_updates",
                {
                    "type": "host_status_update",
                    "host_id": host.id,
                    "hostname": host.hostname,
                    "status": host.status,
                    "last_check_time": host.last_check_time.isoformat() if host.last_check_time else None
                }
            )
        except Exception as e:
            print(f"广播状态变化失败: {e}")
